#!/usr/bin/python

import os, sys, glob, time, shutil, logging, pwd, re
from ftplib import FTP
import datetime as _dt





####################################################################
#VARIABLE DECLERATIONS:
####################################################################



localtime = time.localtime()

time_string = time.strftime('%d.%m.%Y-%H.%M.%S', localtime)

__to_find = dict( ANY_DIGIT=re.compile(r'[0-9]'),
                 ANY_SMALLCASE=re.compile(r'[a-z]'),
                 ANY_BACKUP=re.compile(r'(back|backup)', re.I)
                )

script_location = os.path.dirname(os.path.abspath(__file__)) #path of script for relative traversal
encryption_source = os.path.join(script_location, 'encryption_source') 
decryption_key = os.path.join(encryption_source, 'private.pem')
encrypted_credentials = os.path.join(encryption_source, 'credentials.ssl')
decrypted_output = os.path.join(encryption_source, 'decrypted.txt')
files_source = os.path.join(os.path.dirname(script_location), 'data')
pattern = '{0000000#END#'
archive_days2expire = 3
result_file = os.path.join(files_source, 'numberOfCards')
data_dir = glob.glob(os.path.join(files_source, '*'))
files = []
file_hits = 0
archiving = False


####################################################################
#FUNCTION DECLERATIONS:
####################################################################


def get_current_uid_gid():
    '''
    Unix specific:
    checks environment variable for 'user name'.
    following checks passwd file for 'user name' entry,
    filters through this entry and returns UID and GID
    '''

    user = os.getenv('USER')
    pwstats = pwd.getpwnam(user)
    uid = pwstats.pw_uid
    gid = pwstats.pw_gid
    return (uid, gid)

def is_file_owner(fyle):
    '''
    True: if user and group owner
    False: otherwise
    '''

    stats = os.stat(fyle)
    uid, gid = get_current_uid_gid()
    if uid == stats.st_uid and gid == stats.st_gid:
        return True
    return False

def has_digits(text):
    '''
    searches a string for the presense of a digit
    on finding the pattern return True
    else return False
    '''

    result = __to_find['ANY_DIGIT'].search(text)
    if result:
        return True
    return False
    
def is_backup(text):
    '''
    searches a string for the presense of a digit
    on finding the pattern return True
    else return False
    '''

    result = __to_find['ANY_BACKUP'].search(text)
    if result:
        return True
    return False
    
def has_smallcase(text):
    '''
    searches a string for the presense of a digit
    on finding the pattern return True
    else return False
    '''

    result = __to_find['ANY_SMALLCASE'].search(text)
    if result:
        return True
    return False

def emb_check(fyle):
    '''
    Checks file list for certain conditions to be met.
    If conditions are met, then return True.
    Otherwise they are not embossing files, and return False
    '''

    if '.py' not in rel_fil and not has_digits(rel_fil) and not has_smallcase(rel_fil) and not is_backup(rel_fil) and '_' in rel_fil:
        return True
    return False

def _test_regex():
    '''
    Test regex patterns against a list of strings
    '''
    test_values = ('hello there my name is mahdi',
                    'yea, call me at 9 oclock.',
                    'my number is 97768873.',
                    'i hope you got my back!',
                    'did you do the backup last night?',
                    'back backup 9',
                    'HELLO THERE MY NAME IS MAHDI.',
                    'YEA, CALL ME AT 9 OCLOCK.',
                    'MY NUMBER IS 97768873.',
                    'I HOPE YOU GOT MY BACK!',
                    'DID YOU DO THE BACKUP LAST NIGHT?',
                    'BACK BACKUP 9'
                    )
    for tv in test_values:
        print('ANY_DIGITS: %s'% tv)
        if has_digits(tv):
            print('**has digits')
        else:
            print('**no digits')
        print('ANY_SMALLCASE: %s'% tv)
        if has_smallcase(tv):
            print('**has smallcase')
        else:
            print('**no smallcase')
        print('ANY_BACKUP: %s'% tv)
        if is_backup(tv):
            print('**is backup')
        else:
            print('**not backup')


def _test_owner():
    '''
    file user and group ownership tests
    True: if user and group owner
    False: otherwise
    '''
    from pprint import pprint
    from glob import glob
    os.chdir('../..')
    files = glob('*')
    #pprint(files)
    for f in files:
        print('%s: %s'% (f, is_file_owner(f)))



def safe_fremove(fyle, logger=None):
    def pnt(msg):
        print(str(msg))
    if logger is None:
        report = pnt
    else:
        report = logger.error
    try:
        os.remove(fyle)
    except (OSError, IOError), e:
        report('Failed to remove file: %s  -- %s'% (fyle, str(e)))

#TO BE CHANGED shutils instead of os
def safe_dremove(fyle, logger=None):
    def pnt(msg):
        print(str(msg))
    if logger is None:
        report = pnt
    else:
        report = logger.error
    try:
        os.remove(fyle)
    except (OSError, IOError), e:
        report('Failed to remove file: %s  -- %s'% (fyle, str(e)))

        

def nDaysUp(file_mtime, days=0):
    '''
    This method will compare a files mtime to the cutt of time if it exceeds it will return true, else false.
    file_mtime: modification time of file (os.stat(file).st_mtime)
    days: used to create delta time from today. anything out of this range will return true.
    '''
    try:
      days = int(days)
      file_mtime = int(file_mtime)
      if days < 0:
        raise ValueError
    except (TypeError, ValueError), e:
      print(e)
    dt_today = _dt.datetime.today()
    dt_file = _dt.datetime.fromtimestamp(file_mtime)
    dt_diff = dt_today - dt_file
    if dt_diff.days >= days:
      return True
    return False

def cleanArchive():
    '''
    This method will remove any archives that have passed cutt off point.
    cutt off point is used by nDaysUp method to filter files for deletion (True --> delete)
    '''
    dirs = glob.glob(os.path.join(files_source, 'emb*'))
    logger.debug('Checking for outdated archives: %s days old and upwards'% (archive_days2expire))
    for dir in dirs:
        if not os.path.isdir(dir):
            continue
        dir_mtime = os.stat(dir).st_mtime
        if nDaysUp(file_mtime=dir_mtime, days=archive_days2expire): 
            try:
                shutil.rmtree(dir)
            except (OSError, IOError), e:
                logger.error(str(e))
            logger.warn('Deleting %s'% (dir))


def logger_init():
    bytes = 1048576  #megaBytes
    log_file = 'emb.log'
    logger = logging.getLogger('embLogger')
    logger.setLevel(logging.DEBUG)
    frmt = logging.Formatter(fmt='%(levelname)s - %(asctime)s - %(message)s', datefmt='%d.%m.%Y-%H.%M.%S')
    try:
        hndl = logging.FileHandler(log_file, mode='w')
        hndl.setFormatter(frmt)
    except (OSError, IOError), e:
	sys.stderr.write('Failed to create logging file %s'% (log_file))
    logger.addHandler(hndl)
    return logger



####################################################################
#MAIN DECLERATION:
####################################################################

#write mode: new log for every batch
logger = logger_init()
#archive_days2expire controls the days the archive lives.
if archiving:
    cleanArchive()

for item in data_dir:
    if os.path.isfile(item):
        files.append(item)

results = dict()


for file_name in files:
    try:
        fd = open(file_name, 'r')
        results[file_name] = fd.read().count(pattern)
        if results[file_name] > 0: 
            file_hits += 1
        fd.close()
    except (OSError, IOError), e:
        logger.error(str(e))


#Exit prematurely - no cards to process  
if file_hits == 0:
    logger.debug('No cards were found... Exitting script')	
    sys.exit()

#Create directory for new files to be put into and archived
if archiving:
    to_archive_dir = os.path.join(files_source, 'emb_'+time_string)
    os.mkdir(to_archive_dir)

try:
    fd = open(result_file, 'w+')
except (OSError, IOError), e:
    logger.error('Could not open report file %s'% (str(e)))

toCopy = []
toCopy.append(result_file)

#1
logger.debug('checking files for records and saving results.....')

#TOO MUCH OVER HEAD MOVE FILTER TO AFTER GLOB, TO AVOID READING NONE EMB FILES

for abs_fil, count in results.items():
    base_dir, rel_fil = os.path.split(abs_fil) 
    if count > 0 and emb_check(rel_fil): #check docstring for emb_check [CONDITION FOR CHECKING VALID EMBOSSING FILES]
        fd.write('%s: %s\n'%(rel_fil,count))
        toCopy.append(abs_fil) 

fd.close()



#use formater for prettier text
raw_command = '/usr/bin/openssl rsautl -decrypt -inkey %s -in %s -out %s'%(decryption_key, encrypted_credentials, decrypted_output) 


#USE THIS WHEN HIGHER VERSION OF PYTHON USED -- os.system depreciated for python 2.4+
'''
formated_command = shlex.split(raw_command)
process = subprocess.call(formated_command)
'''

logger.debug('decrypting ftp login information........') 
return_code = os.system(raw_command) #catch return code for ERROR checking
if return_code == 0:
    logger.debug('successful decryption.\n')
try:
    credential_fd = open(decrypted_output, 'r')
except (OSError, IOError), e:
    logger.error('Could not open credentials %s'% (str(e)))
    logger.debug('check directory permissions for write access')

#read file and put credentials into array: 0. host 1. user 2. pass

credentials = []

for i in credential_fd:
    credentials.append(i) 
credential_fd.close()

logger.debug('credential retrieval complete')

try:
    os.remove(decrypted_output)
except (OSError, IOError), e:
    logger.error('Failed to remove file: %s -- %s'% (decrypted_output, str(e)))

#get rid of \n newlines
for n,i in enumerate(credentials):
    credentials[n] = i.rstrip('\n')


if archiving: 
    for file_name in toCopy:
        try:
            shutil.copy2(file_name, to_archive_dir) 
        except (OSError, IOError), e:
            logger.error('Failed to copy %s to %s -- %s'% (file_name, to_archive_dir, str(e)))

ftp = FTP(credentials[0])
ftp.login(credentials[1], credentials[2])
ftp.set_pasv(True)

for abs_fil in toCopy:
    dir, rel_fil = os.path.split(abs_fil)

    try:
        fd_toCopy = open(abs_fil, 'r')
    except (OSError, IOError), e:
        logger.error('Could not open file for FTP %s'% (str(e)))

    if 'numberOfCards' in rel_fil:
        ftp.storbinary('STOR %s_%s'%(rel_fil, ''.join([time_string,'.txt'])), fd_toCopy)
        fd_toCopy.close()

        try:
            os.remove(abs_fil)
        except (OSError, IOError), e:
            logger.error('Failed to delete %s -- %s'% (abs_fil, str(e)))

        logger.debug('FILE: %s      *********    SENT -- BACKED UP AND DELETED LOCALLY\n'%(rel_fil))
        continue

    ftp.storbinary('STOR %s_%s'%(rel_fil, time_string), fd_toCopy)
    fd_toCopy.close()

    try:
        os.remove(abs_fil)
    except (OSError, IOError), e:
        logger.error('Failed to remove %s -- %s'% (abs_fil, str(e)))

    logger.debug('FILE: %s      *********    SENT -- BACKED UP AND DELETED LOCALLY\n'%(rel_fil))

ftp.close()


